from brick.lib.base import *
from pyglons.state import StateManager, State

import pymunk as pm
from pymunk import Vec2d

from math import sin, cos

import logging 
log = logging.getLogger(__name__)


def drawCircle(radius):
    glBegin(GL_LINE_LOOP)
    for angle in range(0, 360, 45):
        glVertex2f(sin(angle) * radius,cos(angle) * radius)
    glEnd()


class Ball(pm.Circle):
    pass

class Brick(pm.Poly):
    pass


class BaseLabelState(BaseState):

    def __init__(self, parent, *args):
        self.parent = parent
        opts = dict(halign='center', valign='center')
        labels = self.labels = []
        for idx, label in enumerate(args):
            if idx == 0:
                font_size = 36
            else:
                font_size = 12
            labels.append( pyglet.text.Label(label, 
                            font_size=font_size, **opts) )
        
    def on_resize(self, width, height):
        for idx, label in enumerate(self.labels):
            if idx == 0:
                label.x = width / 2
                label.y = height / 2
            else:
                label.x = self.labels[0].x
                label.y = self.labels[idx-1].y - 40

    def on_draw(self):
        self.parent.on_draw()
        [l.draw() for l in self.labels]
        return True

    def update(self, dt):
        pass


class ReadyState(BaseLabelState):

    def __init__(self, parent):
        BaseLabelState.__init__(self, parent, 
                "READY?", 
                "press space to play")
        self.color = 255.0
        self.dir = 1

    def on_key_press(self, symbol, modifier):
        if symbol == key.SPACE:
            self.parent.replaceCurrentState(GameState(self.parent))           

    """
    def update(self, dt):
        self.color += self.dir * dt * 500
        if self.dir == 1 and self.color > 255:
            self.dir = -1
            self.color = 255.0
        elif self.dir == -1 and self.color <= 0:
            self.dir = 1
            self.color = 0.0
        c = int(self.color)
        #self.label.color = self.label2.color = (c, c, c, 255)
    """


class PausedState(BaseLabelState):
    def __init__(self, parent):
        BaseLabelState.__init__(self, parent, 
                "PAUSE",
                "press a key to continue")

    def on_key_press(self, symbol, modifier):
        self.parent.popState()
        return True


class LevelOverState(BaseLabelState):
    def __init__(self, parent):
        BaseLabelState.__init__(self, parent,
                "SCORE %d" % parent.points,
                "press space for the next level")

        self.parent.createBricks()
        self.parent.createBall()

    def on_key_press(self, symbol, modifier):
        if symbol == key.SPACE:
            self.parent.replaceCurrentState(GameState(self.parent))           


class LifeLostState(BaseLabelState):
    def __init__(self, parent):
        BaseLabelState.__init__(self, parent,
            "DIED ;..(",
            "press space to go again")

        parent.labels['lives'].text = "lives: %d" % parent.lives

    def on_key_press(self, symbol, modifier):
        if symbol == key.SPACE:
            self.parent.replaceCurrentState(GameState(self.parent))           


class GameOverState(BaseLabelState):
    def __init__(self, parent):
        BaseLabelState.__init__(self, parent, 
            "GAME OVER",
            "press any key to continue")

    def on_key_press(self, symbol, modifier):
        app.popState()
        return True
            

class GameState(BaseState):

    def __init__(self, parent):
        self.parent = parent

    def update(self, dt):
        parent = self.parent
        parent.bricks_to_delete = set()
        parent.space.step(dt)        
        for brick in parent.bricks_to_delete:
            parent.space.remove(brick)
            parent.bricks.remove(brick)

        if len(parent.bricks) == 0:
            parent.replaceCurrentState(LevelOverState(self.parent))

        if parent.ball.body.position[1] < parent.paddle.body.position[1] - 5:
            parent.lives -= 1
            if parent.lives <= 0:
                parent.replaceCurrentState(GameOverState(self.parent))
            else:
                parent.createBall()
                parent.replaceCurrentState(LifeLostState(self.parent))


class BrickGameState(BaseState, StateManager):
    create_keyStateHandler = True

    def __init__(self):
        StateManager.__init__(self)

        self.game_width = 640
        self.game_height = 480

        pm.init_pymunk()

        space = self.space = pm.Space()
        space.gravity = Vec2d(0.0, 0.0)

        space.resize_static_hash()
        space.resize_active_hash()

        space.set_default_collisionpair_func(self.coll_func, None)

        self.ball = None
        self.createBall()

        paddle = self.paddle = self.createPaddle()
        paddle.body.position = Vec2d(self.game_width/2.0, 40)

        self.createWalls()
        self.createBricks()

        self.point_labels = []

        self.points = 0
        self.lives = 3

        self.labels = {
            'points': pyglet.text.Label("0", x=5, y=5),
            'lives': pyglet.text.Label('lives: %d' % self.lives, halign="right", y=5),
        }

    
    def on_resize(self, width, height):
        self.labels['lives'].x = width - 5

    def justMadeActive(self):
        BaseState.justMadeActive(self)
        self.pushState(ReadyState(self))

    def justMadeInactive(self):
        BaseState.justMadeInactive(self)

        if self.state:
            self.state.justMadeInactive()

    def remove_label(self, dt, label):
        self.point_labels.remove(label)

    def coll_func(self, shapeA, shapeB, contacts, normal_coef, data):
        if isinstance(shapeA, Ball) and isinstance(shapeB, Brick):
            self.bricks_to_delete.add(shapeB)
            self.points += 100
            self.labels['points'].text = "%d" % self.points
            p = contacts[0].position
            label = pyglet.text.Label("100", x = p[0], y=p[1], halign="center",
                    font_size=10)
            self.point_labels.append(label)
            pyglet.clock.schedule_once(self.remove_label, 1.0, label)
        return True

    def createBricks(self):
        w, h = 20, 10
        verts = [Vec2d(-w,-h), Vec2d(-w, h), Vec2d(w, h), Vec2d(w,-h)]

        self.bricks = []

        for x in range(1, 10):
            for y in range(1, 5):
                body = pm.Body(1e100, 1e100)
                shape = Brick(body, verts, Vec2d(0,0))
                shape.elasticity = 1.0
                body.position = Vec2d(x * ((w*2+20)), self.game_height - 20 - (((h*2)+20)*y))
                self.space.add(shape)
                self.bricks.append(shape)

    def createWalls(self):
        gw, gh = self.game_width, self.game_height
        walls = [
            ((0, 0), (0, gh)),
            ((gw, 0), (gw, gh)),
            ((0, 0), (gw, 0)),
            ((0, gh), (gw, gh)),
        ]
        body = pm.Body(1e100, 1e100)
        for p1, p2 in walls:
            seg = pm.Segment(body, Vec2d(*p1), Vec2d(*p2), 5.0)
            seg.elasticity = 1.0
            self.space.add(seg)

    def createBall(self):
        if self.ball:
            self.space.remove(self.ball, self.ball.body)

        mass = 1
        radius = 5
        inertia = pm.moment_for_circle(mass, 0, radius, Vec2d(0, 0))
        body = pm.Body(mass, inertia)
        shape = Ball(body, radius, Vec2d(0, 0))
        shape.elasticity = 1.0
        self.space.add(body, shape)
        body.position = Vec2d(self.game_width/2.0, self.game_height/3.0)
        body.apply_impulse(Vec2d(150.0, 150.0), Vec2d(0,0))
        self.ball = shape
        return shape

    def createPaddle(self):
        w, h = 20, 5
        verts = [Vec2d(-w,-h), Vec2d(-w, h), Vec2d(w, h), Vec2d(w,-h)]

        body = pm.Body(1e100, 1e100)
        shape = pm.Poly(body, verts, Vec2d(0,0))
        shape.elasticity = 1.0
        self.space.add(shape)
        return shape

    def on_key_press(self, symbol, modifier):
        if symbol == key.P:
            self.pushState(PausedState(self))

    def on_draw(self):
        glClear(GL_COLOR_BUFFER_BIT)
        glLoadIdentity()

        glColor3f(1.0, 1.0, 1.0)

        p = self.ball.body.position
        glPushMatrix()
        glTranslatef(p[0], p[1], 0.0)        
        drawCircle(5.0)
        glPopMatrix()

        p = self.paddle.body.position
        glPushMatrix()
        glTranslatef(p[0], p[1], 0.0)
        w, h = 20, 5
        glBegin(GL_QUADS)
        glVertex2i(-w, -h)
        glVertex2i(-w,  h)
        glVertex2i( w,  h)
        glVertex2i( w, -h)
        glEnd()
        glPopMatrix()

        w, h = 20, 10
        for brick in self.bricks:
            glPushMatrix()
            p = brick.body.position
            glTranslatef(p[0], p[1], 0.0)
            glBegin(GL_LINE_LOOP)
            glVertex2i(-w, -h)
            glVertex2i(-w,  h)
            glVertex2i( w,  h)
            glVertex2i( w, -h)
            glEnd()
            glPopMatrix()

        for label in self.point_labels:
            label.draw()

        for label in self.labels.itervalues():
            label.draw()

    @update_time(0.001)
    def on_update(self, dt):
        if self.keys[key.RIGHT]:
            self.paddle.body.position[0] += 200.0 * dt
        elif self.keys[key.LEFT]:
            self.paddle.body.position[0] -= 200.0 * dt

        self.state.update(dt)


